<?php

	# Directory Separator shortcut
	if (!defined('DS'))
		define('DS', DIRECTORY_SEPARATOR);
	
	# Inclusions
	require_once	'Smarty-3.1.13/libs/Smarty.class.php';
	include_once	'cssToInlineStyles/css_to_inline_styles.php';	
	

	/**
	 * SmartyMailer class
	 * Create beautiful mails using Smarty
	 * Design them with standard CSS stylesheets
	 * SmartyMailer will convert your CSS to inline styles for correct rendering into e-mail clients
	 * 
	 * Example Usage :
	 * 	Displaying :
	 * 		$SmartyMailer	=	new SmartyMailer();
	 * 		$SmartyMailer	->	SetTemplateDir('MyTemplateDir')		// Smarty template directory
	 * 		$SmartyMailer	->	SetCssDir('MyCssDir')				// Css directory
	 * 		$SmartyMailer	->	SetTemplateFile('MyTemplate.tpl')	// Smarty template
	 * 		$SmartyMailer	->	AddCssFile('MyStyles.css')			// Css styles
	 * 		$SmartyMailer	->	Assign('FirstName', 'Ben')			// Smarty variable assignments (you can use any Smarty method)
	 * 		$SmartyMailer	->	Process()	->	ShowOutput()		// Processes and displays the result
	 * 
	 * 	Sending :
	 * 		$SmartyMailer	->	SetSender('from@example.com')		// Set email's sender
	 * 		$SmartyMailer	->	SetRecipient('to@example.com')		// Set email's recipient
	 * 		$SmartyMailer	->	SetSubject('Hello {$FirstName}')	// Set email's subject (can be a Smarty string)
	 * 		$SmartyMailer	->	Process()	->	Send()				// Processes and Sends the e-mail
	 * 
	 * Requirements :
	 * 	PHP 5.x
	 * 	Smarty 3.1.x
	 * 	CSS To Inline Styles by Tijs Verkoyen 1.x
	 * 
	 * @author Beno!t POLASZEK - Jan 2013
	 */
	class SmartyMailer extends Smarty {
		
		#############################
		#	CONSTANTS PROPERTIES	#
		#############################
		
		const		SMARTY_COMPILE_DIR		=	'smarty_compile';
		const		SMARTY_CACHE_DIR		=	'smarty_cache';	

		
		#############################
		#	PROTECTED PROPERTIES	#
		#############################
		
		protected	$Sender;
		protected	$Recipient;
		protected	$Template;
		protected	$CssDir;
		protected	$Css;
		protected	$oCss;
		protected	$Html;
		protected	$Output;
		protected	$Subject;
		protected	$Headers;
		protected	$TitleItems				=	Array();
		
		#############################
		#	PUBLIC PROPERTIES		#
		#############################
		
		public		$IsProcessed			=	false;
		public		$IsSent					=	false;
		
			
		#############################
		#			METHODS			#
		#############################

		/**
		 * Constructor : initializes Smarty Cache and set default directories
		 * 
		 * @author Beno!t POLASZEK - Jan 2013
		 */	
		public function __construct() {
			
			parent::__construct();			
			
			$this	->	SetCacheLifetime(0);	
			$this	->	SetCaching(Smarty::CACHING_OFF);
			$this	->	SetCompileDir(realpath(self::SMARTY_COMPILE_DIR));
			$this	->	SetCacheDir(realpath(self::SMARTY_CACHE_DIR));

			
		}
		
		
		/**
		 * Processes the template and converts CSS stylesheet to inline styles
		 * (Without CSS processed)
		 * 
		 * Creates $this->Html with Smarty
		 * Creates $this->Output with CSSToInlineStyles for converting Ids and Classes to inline styles
		 * 
		 * @param bool $Compress (default : true) : Removes comments, spaces and linebreaks from the output		 * 
         * @param bool $Debug : If false, mute Smarty's expected errors
		 * @return SmartyMailer instance for chaining
		 * @author Beno!t POLASZEK - Jan 2013
		 */
		public function Process($Compress = true, $Debug = false) {
			
			# We won't show errors into mails
			if ((bool) !$Debug)		Smarty::MuteExpectedErrors();
			
			# First, let's compile and fetch Smarty's result.
			$this->Fetch();
			
			# Now, if a CSS file has been provided, inject it into inline styles
			if ($this->Html AND $this->Css AND class_exists('CSSToInlineStyles')) :
				$this->oCss		=	new CSSToInlineStyles($this->Html, $this->Css);			
				$this->Output	=	$this->oCss->convert(false);
			
			# Otherwise, just use Smarty's output provided by the current Fetch() method.
			else :
				$this->Output	=	$this->Html;
				
			endif;
			
			# For lowering bandwidth usage, we remove unnecessary stuff
			if ((bool) $Compress) :
			
				$this->Output	=	preg_replace('!/\*[^*]*\*+([^/][^*]*\*+)*/!',	'',		$this->Output);	// Remove comments
				$this->Output	=	preg_replace('![\t ]*[\r\n]+[\t ]*!',			'',		$this->Output);	// Remove spaces and linebreaks
				
			endif;
			
			# Process mail subject
			if ($this->Subject)
				$this->Subject	=	parent::Fetch('string:' . (string) $this->Subject, $this->GetCacheId(), $this->GetCompileId());
			
			# Remember we have processed
			$this->IsProcessed	=	true;
				
			return $this;
		}
		
		/**
		 * Sends the processed template
		 * 
		 * @return SmartyMailer instance for chaining
		 * @author Beno!t POLASZEK - Jan 2013
		 */
		public function Send() {
			
			if (!$this->Sender)
				throw new SmartyException('No Sender has been provided. Use SetSender() method');
			
			if (!$this->Recipient)
				throw new SmartyException('No Recipient has been provided. Use SetRecipient() method');
				
			if (!$this->IsProcessed)
				$this->Process();
				
			$this->Headers	=	'From: ' . (string) $this->Sender . "\r\n";
			$this->Headers	.=	'MIME-Version: 1.0' . "\r\n";
			$this->Headers	.=	'Content-type: text/html; charset=iso-8859-1' . "\r\n";
			
			$this->IsSent	=	(bool) $this->Mail();
				
			return $this;
			
		}
		
		/**
		 * Use PHP mail function by default.
		 * Feel free to overload this method with another mail module
		 * 
		 * @return bool true / false
		 * @author Beno!t POLASZEK - Jan 2013
		 */
		protected function Mail() {
			return mail((string)	$this->Recipient, 
						(string)	$this->Subject, 
						(string)	$this->Output,
						(string)	$this->Headers);
		}
		
		
		
		/**
		 * Displays the template to the client
		 * (Without CSS processed)
		 *  - extends Smarty's Display() method
		 * 
		 * @author Beno!t POLASZEK - Jan 2013
		 */
		public function Display() {
			
			if (!$this->Template)
				throw new SmartyException('No template defined. Use SetTemplateFile() method before calling Fetch() or Display()');
				
			else	
				echo $this	-> Fetch()	-> GetHtml();
				
		}
		
		/**
		 * Fetches the template
		 * (Without CSS processed)
		 *  - extends Smarty's Fetch() method
		 * See ShowOutput() method for combination of Smarty fetched template + inline CSS injecting
		 * 
		 * Creates $this->Html
		 * 
		 * @return SmartyMailer instance for chaining
		 * @author Beno!t POLASZEK - Jan 2013
		 */
		public function Fetch() {
			
			if (!$this->Template)
				throw new SmartyException('No template defined. Use SetTemplateFile() method before calling Fetch() or Display()');
				
			else	
				$this->Html	=	parent::Fetch($this->Template, $this->GetCacheId(), $this->GetCompileId());
				
			return $this;
		}
		
		/**
		 * Shows the final output with Smarty fetched template and CSS styles injected.
		 * Use it for designing your mails before sending them, 
		 * or to control what has been sent after calling the Send() method.
		 * 
		 * Beware some differences may occur with some e-mail clients,
		 * since HTML is rendered differently from Outlook, Hotmail, Gmail, etc.
		 * 
		 * @author Beno!t POLASZEK - Jan 2013
		 */
		public function ShowOutput() {
			
			if (!$this->IsProcessed) :
				throw new SmartyException('SmartyMailer didn\'t process. Use the Process() method to compile first.');
				
			else :
					
				echo $this->GetOutput();	
				
				if ($this->Subject)
					$this->TitleItems[]	=	$this->Subject;
					
				if ($this->Sender)
					$this->TitleItems[]	=	'[From: ' . $this->Sender . ']';
					
					
				if ($this->Recipient)
					$this->TitleItems[]	=	'=> [To: ' . $this->Recipient . ']';
				
				if (count($this->TitleItems) >= 1)	
					echo "<script>document.title = '".	addslashes(implode(' ', $this->TitleItems))	."';</script>";	// Shows Subject in the Title
						
			endif;
		}
		
		/**
		 * Checks if email address is correct
		 * 
		 * @param string $Email
		 * @return bool true / false
		 * @author Beno!t POLASZEK - Jan 2013
		 */
		public static function ValidateEmail($EMail) {
			return (bool) filter_var((string) $EMail, FILTER_VALIDATE_EMAIL);
		}
		
		
		
		
		####	SETTERS		####
		
		/**
		 * Registers the template directory
		 * 	- extends Smarty method SetTemplateDir()
		 * 
		 * @param string $TemplateDir
		 * @return SmartyMailer instance for chaining
		 * @author Beno!t POLASZEK - Jan 2013
		 */
		public function SetTemplateDir($TemplateDir) {			
			parent::SetTemplateDir(array_unique(array_merge(Array(realpath($TemplateDir)), 
																(array) $this->GetTemplateDir())));
				
			return $this;
		}
		
		/**
		 * Registers the CSS directory
		 * 
		 * @param string $CssDir
		 * @return SmartyMailer instance for chaining
		 * @author Beno!t POLASZEK - Jan 2013
		 */
		public function SetCssDir($CssDir) {			
			
			if ((bool) !realpath($CssDir))
				throw new SmartyException('"' . $CssDir . '" is not a valid directory.');
			else	
				$this->CssDir	=	realpath($CssDir);
				
			return $this;
		}
		
		
		/**
		 * Registers the template file
		 * 
		 * @param string $Template
		 * @return SmartyMailer instance for chaining
		 * @author Beno!t POLASZEK - Jan 2013
		 */
		public function SetTemplateFile($Template) {
			
			if (!$this->TemplateExists($Template))
				throw new SmartyException('"'. (string) $Template . '" template cannot be found.');
				
			else
				$this	->	Template	=	$Template;	
				
			return $this;
		}
		
		/**
		 * Registers the CSS file to apply
		 * 
		 * @param string $CssFile
		 * @return SmartyMailer instance for chaining
		 * @author Beno!t POLASZEK - Jan 2013
		 */
		public function SetCssFile($CssFile) {
			
			if (!file_exists($this->CssDir . DS . $CssFile))
				throw new SmartyException('"' . $this->CssDir . DS . $CssFile . '" CSS file cannot be found.');
				
			else
				$this->Css =	file_get_contents($this->CssDir . DS . $CssFile);
			
			return $this;
		}
		
		/**
		 * Adds a CSS file to process
		 * 
		 * @param string $CssFile
		 * @return SmartyMailer instance for chaining
		 * @author Beno!t POLASZEK - Jan 2013
		 */
		public function AddCssFile($CssFile) {
			
			if (!file_exists($this->CssDir . DS . $CssFile))
				throw new SmartyException('"' . $this->CssDir . DS . $CssFile . '" CSS file cannot be found.');
				
			else	
				$this->Css .=	file_get_contents($this->CssDir . DS . $CssFile);
			
			return $this;
		}
		
		/**
		 * Registers the sender
		 * 
		 * @param string $Sender
		 * @return SmartyMailer instance for chaining
		 * @author Beno!t POLASZEK - Jan 2013
		 */
		public function SetSender($Sender) {
			
			if (!self::ValidateEmail($Sender))
				throw new SmartyException('"'. (string) $Sender . '" is not a correct e-mail address.');
				
			else
				$this	->	Sender	=	$Sender;
				
			return $this;
		}
		
		
		/**
		 * Registers the recipient
		 * 
		 * @param string $Recipient
		 * @return SmartyMailer instance for chaining
		 * @author Beno!t POLASZEK - Jan 2013
		 */
		public function SetRecipient($Recipient) {
			
			if (!self::ValidateEmail($Recipient))
				throw new SmartyException('"'. (string) $Recipient . '" is not a correct e-mail address.');
				
			else
				$this	->	Recipient	=	$Recipient;
				
			return $this;
		}
		
		/**
		 * Registers mail's subject.
		 * Can contain smarty variables (take care of single-quoting your string)
		 * 
		 * @param string $Subject
		 * @return SmartyMailer instance for chaining
		 * @author Beno!t POLASZEK - Jan 2013
		 */
		public function SetSubject($Subject) {			
			$this->Subject	=	$Subject;
			return $this;
		}
		
		
		
		
		
		
		
		####	GETTERS		####
		
		
		/**
		 * Retrieves the sender
		 * 
		 * @param string $Sender
		 * @return string SmartyMailer sender
		 * @author Beno!t POLASZEK - Jan 2013
		 */
		public function GetSender($Sender) {
			return $this->Sender;
		}
		
		
		/**
		 * Retrieves the recipient
		 * 
		 * @param string $Recipient
		 * @return string SmartyMailer recipient
		 * @author Beno!t POLASZEK - Jan 2013
		 */
		public function GetRecipient($Recipient) {
			return $this->Recipient;
		}		
		
		
		/**
		 * Retrieves mail's subject.
		 * (Processed or not, depending the use of Process() method before or after)
		 * 
		 * @return string Subject
		 * @author Beno!t POLASZEK - Jan 2013
		 */
		public function GetSubject() {
			return $this->Subject;
		}
		
		/**
		 * Retrieves SmartyMailer HTML, BEFORE injecting CSS into inline HTML
		 * Only works after calling Process() or Fetch() method
		 * 
		 * @return string Html
		 * @author Beno!t POLASZEK - Jan 2013
		 */
		public function GetHtml() {
			return $this->Html;
		}
		
		/**
		 * Retrieves SmartyMailer HTML, AFTER injecting CSS into inline HTML
		 * Only works after calling Process() method
		 * 
		 * @return string Output
		 * @author Beno!t POLASZEK - Jan 2013
		 */
		public function GetOutput() {
			return $this->Output;
		}
			
		
		public function __destruct() {
			parent::__destruct();
		}		
		
	}
	
	